﻿using IceCS.HttpLib.Provider;
using System;
using System.IO;
using System.Net;
using IceCS.HttpLib.Streams;


namespace IceCS.HttpLib
{

    public class Request
    {
        /// <summary>
        /// 请求URL
        /// </summary>
        public string Url { get; set; }
        /// <summary>
        /// 
        /// </summary>
        public HttpVerb Method { get; set; }
        /// <summary>
        /// 
        /// </summary>
        public HeaderProvider Headers { get; set; }
        /// <summary>
        /// 
        /// </summary>
        public AuthenticationProvider Auth { get; set; }

        /// <summary>
        /// 超时时间
        /// </summary>
        public int Timeout { get; set; }

        /// <summary>
        /// 
        /// </summary>
        public BodyProvider Body;
        /// <summary>
        /// 
        /// </summary>
        public ActionProvider Action { get; set; }
        /// <summary>
        /// 
        /// </summary>
        public Request()
        {
            Method = HttpVerb.Get;
        }
        /// <summary>
        /// 
        /// </summary>
        public void Go()
        {
            MakeRequest();
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="url"></param>
        /// <returns></returns>
        protected virtual HttpWebRequest GetWebRequest(string url)
        {
            return (HttpWebRequest)WebRequest.Create(new Uri(url));
        }

        /// <summary>
        /// 
        /// </summary>
        protected void MakeRequest()
        {
            if (string.IsNullOrWhiteSpace(Url))
            {
                throw new ArgumentNullException("url is empty");
            }

            try
            {
                /*
                 * Create new Request
                 */
                HttpWebRequest request = this.GetWebRequest(Url);
                request.CookieContainer = Cookies.Container;
                request.Method = Method.ToString().ToUpper();
                request.Timeout = Timeout;
                if (Action != null && Action.Make != null)
                    Action.Make(request); //Pass the request out

                if (Headers != null)
                {
                    request.Headers = GetHeadersFromProvider(request.RequestUri, Headers.GetHeaders());
                }

                if (Method == HttpVerb.Get || Method == HttpVerb.Head || Body == null)
                {
                    ExecuteRequestWithoutBody(request);
                }
                else
                {
                    request.ContentType = Body.GetContentType();
                    ExecuteRequestWithBody(request);
                }
            }
            catch (WebException webEx)
            {
                Action.Fail(webEx);
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="uri"></param>
        /// <param name="headers"></param>
        /// <returns></returns>
        private static WebHeaderCollection GetHeadersFromProvider(Uri uri, Header[] headers)
        {
            WebHeaderCollection whc = new WebHeaderCollection();

            foreach (Header h in headers)
            {
                if (h.Name.ToUpperInvariant() == "COOKIE")
                {
                    var cookiePairs = h.Value.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries);
                    foreach (var cookiePair in cookiePairs)
                    {
                        var index = cookiePair.IndexOf('=');

                        Cookies.Container.Add(uri, new Cookie(cookiePair.Substring(0, index), cookiePair.Substring(index + 1, cookiePair.Length - index - 1)) { Domain = uri.Host });
                    }
                }
                else
                {
                    whc[h.Name] = h.Value;
                }
            }

            return whc;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="request"></param>
        protected virtual void ExecuteRequestWithoutBody(HttpWebRequest request)
        {
            request.BeginGetResponse(ProcessCallback(Action.Success, Action.Fail), request);
        }

        protected virtual void ExecuteRequestWithBody(HttpWebRequest request)
        {
            request.BeginGetRequestStream(new AsyncCallback((IAsyncResult callbackResult) =>
            {
                HttpWebRequest tmprequest = (HttpWebRequest)callbackResult.AsyncState;


                ProgressCallbackHelper copy = Body.GetBody().CopyToProgress(tmprequest.EndGetRequestStream(callbackResult), null);
                copy.ProgressChanged += (bytesSent, totalBytes) => { Body.OnProgressChange(bytesSent, totalBytes); };
                copy.Completed += (totalBytes) => { Body.OnCompleted(totalBytes); };
                copy.Go();

                // Start the asynchronous operation to get the response
                tmprequest.BeginGetResponse(ProcessCallback(Action.Success, Action.Fail), tmprequest);


            }), request);
        }


        protected AsyncCallback ProcessCallback(Action<WebHeaderCollection, Stream> success, Action<WebException> fail)
        {
            return new AsyncCallback((callbackResult) =>
            {
                HttpWebRequest webRequest = (HttpWebRequest)callbackResult.AsyncState;

                try
                {
                    using (HttpWebResponse response = (HttpWebResponse)webRequest.EndGetResponse(callbackResult))
                    {
                        // if (response.ContentLength > 0) { response.Headers.Add("Content-Length", response.ContentLength.ToString()); }
                        if (success != null)
                            success(response.Headers, response.GetResponseStream());
                    }

                }
                catch (WebException webEx)
                {
                    fail(webEx);
                }
            });
        }
    }
}
